差量更新(增量更新)的原理是什么?
考察点
- block map 的工作机制
- 差量失败的回退策略
参考答案
1. 为什么需要差量更新
Electron 应用全量包通常 80-200MB,但一次版本迭代实际变更的代码可能只有几 MB(业务 JS)。每次都全量下载浪费带宽、拉长更新时长,agent 大规模分发时 CDN 流量成本也可观。
2. 核心原理:block map(块映射)
electron-builder 打包时对安装包生成 block map 文件(.blockmap):
原理类似 zsync/rsync:
1. 把文件切成定长块(block)
2. 计算每块的哈希(校验和 + 压缩信息)
3. blockmap 文件 = 所有块的哈希清单(本身很小,几十 KB)
更新过程:
1. 客户端下载新版的 blockmap(很小)
2. 扫描本地旧版文件,计算各块哈希
3. 与新版 blockmap 比对,找出"本地已有的块"和"必须下载的块"
4. 通过 HTTP Range 请求只下载变更的块
5. 本地旧块 + 新下载的块拼接成完整新版安装包
6. sha512 整体校验 → 走正常安装流程
关键点:差量计算发生在客户端,服务端只需静态托管全量包 + blockmap,不需要为每个旧版本生成差分包——这是它区别于"补丁包"方案(如 bsdiff)的地方。
3. 产物与配置
electron-builder 输出:
MyAgent-Setup-1.2.0.exe # 全量安装包
MyAgent-Setup-1.2.0.exe.blockmap # 块映射
latest.yml # 元数据(含 sha512)
默认开启,无需配置;electron-updater 自动优先尝试差量:
// latest.yml 中同时记录了差量信息
autoUpdater.on('update-available', () => {
// 内部逻辑:先读 blockmap 尝试差量下载
autoUpdater.downloadUpdate();
});
4. 差量失效与回退
以下情况差量无法命中,自动回退为全量下载(electron-updater 内置逻辑):
- 本地文件损坏/被修改,块哈希对不上
- blockmap 下载失败或解析失败
- 跨版本过多,旧版本块与新版差异过大(差量无收益)
- 本地版本太老没有参与计算的基线
回退对用户透明,只是下载体积变大。
5. 平台差异
- Windows(nsis):支持差量,效果最明显(Electron 应用大头是 chromium 运行时,业务更新时大部分块可复用)
- macOS:官方更新机制(Squirrel.Mac)不支持差量,只能全量 zip。要差量需自研更新方案(如 Sparkle 的 delta update 或完全自定义),成本高,多数团队接受全量
6. 自研差量(进阶话题)
如果对 blockmap 的收益不满足,业界有更深方案:
- bsdiff/bspatch:二进制 diff 算法生成补丁包,体积最小,但需要服务端为"每个旧版本 → 新版本"组合生成补丁,组合爆炸,通常只对最近 N 个版本生成
- 资源级更新:把业务代码(app.asar)与运行时分离,小版本只更新 asar(几 MB),运行时不变——这是很多团队的务实选择,等于把差量粒度提升到文件级
面试话术
electron-builder 的差量基于 block map:定长分块 + 块哈希,客户端用 Range 请求只拉变更块再本地重组。它是客户端计算模式,服务端静态托管即可。注意三点:sha512 整体校验兜底、失败自动回退全量、macOS 官方机制不支持差量——这也是很多团队把 asar 与运行时拆分做文件级更新的原因。
常见追问
- 差量能省多少流量? 纯业务代码更新通常省 70-90%;升级 Electron 大版本(运行时变了)几乎省不了。
- blockmap 被篡改怎么办? 最终产物有 sha512 校验(latest.yml 中),且建议 yml 走 https + CDN 防篡改,重组后校验不过直接废弃。